package pl.edu.mimuw.irs.rcp.controller.editor.workflow;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.gef.ContextMenuProvider;
import org.eclipse.gef.DefaultEditDomain;
import org.eclipse.gef.EditDomain;
import org.eclipse.gef.EditPartViewer;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CommandStackListener;
import org.eclipse.gef.editparts.ScalableFreeformRootEditPart;
import org.eclipse.gef.tools.SelectionTool;
import org.eclipse.gef.ui.actions.ActionRegistry;
import org.eclipse.gef.ui.actions.StackAction;
import org.eclipse.gef.ui.actions.UpdateAction;
import org.eclipse.gef.ui.parts.ScrollingGraphicalViewer;
import org.eclipse.gef.ui.properties.UndoablePropertySheetEntry;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.properties.IPropertySheetPage;
import org.eclipse.ui.views.properties.PropertySheetPage;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertySheetPageContributor;

import pl.edu.mimuw.irs.core.model.design.net.IRSWorkflow;
import pl.edu.mimuw.irs.rcp.controller.editor.IWorkflowEditor;
import pl.edu.mimuw.irs.rcp.controller.editor.workflow.dnd.TextTransferDropTargetListener;
import pl.edu.mimuw.irs.rcp.controller.editor.workflow.menu.WorkflowContextMenuProvider;
import pl.edu.mimuw.irs.rcp.controller.editor.workflow.part.EditPartFactory;
import pl.edu.mimuw.irs.rcp.controller.editor.workflow.part.IRSNodeEditPart;
import pl.edu.mimuw.irs.rcp.persistence.IPersistence;
import pl.edu.mimuw.irs.rcp.persistence.PersistenceAdapter;
import pl.edu.mimuw.irs.rcp.persistence.PersistenceManager;
import pl.edu.mimuw.irs.rcp.util.Logger;
import pl.edu.mimuw.irs.rcp.util.MessageCode;
import pl.edu.mimuw.irs.rcp.util.UITexts;

/*
 * @author Jakub Rauch
 * Created on: 2009-05-17
 */
public class IRSWorkflowEditor extends EditorPart implements IWorkflowEditor, ITabbedPropertySheetPageContributor {

	// *** Static attributes
	
	public static final String ID = "pl.edu.mimuw.irs.editor.irsworkflow";
	public static final String CONTRIBUTOR_ID = "pl.edu.mimuw.irs.workflow.part.requirement";
	
	// *** Attributes
	private IRSWorkflow model;
	private EditDomain editDomain;
	//private IWorkbenchPart lastPart;
	private boolean dirty;
	private PropertySheetPage propertySheetPage;
	private CommandStackListener commandStackListener = new CommandStackListener() {
		public void commandStackChanged(EventObject event) {
			updateActions(stackActionIDs);
			setDirty(getCommandStack().isDirty());
		}
	};
	private ISelectionListener selectionListener = new ISelectionListener() {
		public void selectionChanged(IWorkbenchPart part, ISelection selection) {
			//if (lastPart == part) return;
			//lastPart = part;
			//if (!(selection instanceof StructuredSelection)) return;
			//StructuredSelection structuredSelection = (StructuredSelection) selection;
			//Object first = structuredSelection.getFirstElement();
			//if (first == null || !(first instanceof IRSNodeEditPart)) return;
			//IRSNodeEditPart editPart = (IRSNodeEditPart) first;
			//highlightCollapsing(editPart);
			//updateActions(editPartActionIDs);
		}
	};
	private GraphicalViewer graphicalViewer;
	private EditPartFactory editPartFactory;
	private ActionRegistry actionRegistry;
	//private List<String> editPartActionIDs = new ArrayList<String>();
	private List<String> stackActionIDs = new ArrayList<String>();
	private Set<IRSNodeEditPart> collapsing;
	//private List<String> editorActionIDs = new ArrayList<String>();
	// Getters and setters
	public IRSWorkflow getModel() {
		return model;
	}
	public EditDomain getEditDomain() {
		if (editDomain == null) editDomain = new DefaultEditDomain(this);
		return editDomain;
	}
	protected PropertySheetPage getPropertySheetPage() {
		if (propertySheetPage == null) {
			propertySheetPage = new PropertySheetPage();
			propertySheetPage.setRootEntry(
					new UndoablePropertySheetEntry(getCommandStack()));
		}
		return propertySheetPage;
	}
	public CommandStack getCommandStack() {
		return getEditDomain().getCommandStack();
	}
	protected CommandStackListener getCommandStackListener() {
		return commandStackListener;
	}
	public GraphicalViewer getGraphicalViewer() {
		return graphicalViewer;
	}
	public EditPartFactory getEditPartFactory() {
		if (editPartFactory == null) editPartFactory = new EditPartFactory();
		return editPartFactory;
	}
	public ActionRegistry getActionRegistry() {
		if (actionRegistry == null) actionRegistry = new ActionRegistry();
		return actionRegistry;
	}
	protected ISelectionListener getSelectionListener() {
		return selectionListener;
	}
//	protected void addEditPartAction(SelectionAction action) {
//		getActionRegistry().registerAction(action);
//		editPartActionIDs.add(action.getId());
//	}
	protected void addStackAction(StackAction action) {
		getActionRegistry().registerAction(action);
		stackActionIDs.add(action.getId());
	}
//	protected void addEditorAction(EditorPartAction action) {
//		getActionRegistry().registerAction(action);
//		editorActionIDs.add(action.getId());
//	}
	protected void addAction(IAction action) {
		getActionRegistry().registerAction(action);
	}
	public Set<IRSNodeEditPart> getCollapsing() {
		if (collapsing == null) return new LinkedHashSet<IRSNodeEditPart>();
		return collapsing;
	}
	
	// *** Constructors
	
	public IRSWorkflowEditor() {
		getEditDomain().setActiveTool(new SelectionTool());
	}
	
	// *** Methods

	public void createPartControl(Composite parent){
		graphicalViewer = createGraphicalViewer(parent);
	}
	
	@Override
	public void setFocus() {
	}
	
	@Override
	public void doSave(IProgressMonitor monitor) {
		IEditorInput editorInput = getEditorInput();
		
		monitor.beginTask(UITexts.get(MessageCode.WORKFLOW_SAVE_PROGRESS), IProgressMonitor.UNKNOWN);
		
		if (editorInput instanceof FileEditorInput) {
			FileEditorInput fileEditorInput = (FileEditorInput) editorInput;
			
			IPersistence<IRSWorkflow> workflowPersistence = PersistenceAdapter.getManager(IRSWorkflow.class);
			InputStream source;
			try {
				source = workflowPersistence.objectAsInputStream(getModel());
				fileEditorInput.getFile().setContents(source, IResource.FORCE, null);
			} catch (CoreException e) {
				Logger.handle(MessageCode.WORKFLOW_SAVE_FILE_PROBLEM, e);
				return;
			} catch (Exception e) {
				Logger.handle(MessageCode.WORKFLOW_SAVE_SERIALIZE_PROBLEM, e);
				return;
			}
		} else {
			Logger.handle(MessageCode.WORKFLOW_SAVE_INPUT_PROBLEM, editorInput.getClass(), editorInput.getName());
			return;
		}
		
		

		/*
			IPersistence<Roles> persistence = PersistenceAdapter.getManager(Roles.class);
			InputStream source;
			try {
				source = persistence.objectAsInputStream(getRoles());
				fileEditorInput.getFile().setContents(source, IResource.FORCE, null);
				
				PersistenceManager manager = PersistenceManager.getInstance(editorInput);
				if (manager == null) return;
				manager.clearRoles();
				
			} catch (CoreException e) {
				Logger.handle(MessageCode.ROLES_SAVE_FILE_PROBLEM, e);
				return;
			} catch (Exception e) {
				Logger.handle(MessageCode.ROLES_SAVE_SERIALIZE_PROBLEM, e);
				return;
			}
		} else {
			Logger.handle(MessageCode.ROLES_SAVE_INPUT_PROBLEM, editorInput.getClass(), editorInput.getName());
			return;
		}*/
		
		
		
		monitor.done();
		
		// update CommandStack
		getCommandStack().markSaveLocation();
	}
	
	@Override
	public void doSaveAs() {
		// XXX : save as

		// update CommandStack
		getCommandStack().markSaveLocation();
	}
	
	@Override
	public boolean isDirty() {
		return dirty;
	}
	
	protected void setDirty(boolean dirty) {
		if (this.dirty != dirty) {
			this.dirty = dirty;
			firePropertyChange(IEditorPart.PROP_DIRTY);
		}
	}
	
	@Override
	public boolean isSaveAsAllowed() {
		return true;
	}
	
	public void init(IEditorSite site, IEditorInput input) throws PartInitException {
		// store site and input
		setSite(site);
		setInput(input);
		// add CommandStackListener
		getCommandStack().addCommandStackListener(getCommandStackListener());
		
		// add selection change listener
		getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(
				getSelectionListener());
		
		
		createActions();
	}
	
	@Override
	public void dispose() {
		// remove CommandStackListener
		getCommandStack().removeCommandStackListener(getCommandStackListener());
		// remove selection listener
		getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(
				getSelectionListener());
		// delete action registry
		getActionRegistry().dispose();
		
		try {
			PersistenceManager.getInstance(getEditorInput()).clearWorkflow();
		} catch (Exception e) {
			Logger.handle(MessageCode.WORKFLOW_CLEAR_PROBLEM, e);
		}
		
		super.dispose();
		
	}
	
	protected void createActions()
	{
		
		//addStackAction(new UndoAction(this));
		//addStackAction(new RedoAction(this));
		
		//addEditPartAction(new DeleteAction((IWorkbenchPart) this));
		
		//addEditorAction(new SaveAction(this));
		//addEditorAction(new PrintAction(this));
	}
	
	protected GraphicalViewer createGraphicalViewer(Composite parent) {
		
		GraphicalViewer viewer = new ScrollingGraphicalViewer();
		viewer.createControl(parent);
		
		// configure the viewer
		viewer.getControl().setBackground(new Color(null, 255,255,255));

		viewer.setRootEditPart(new ScalableFreeformRootEditPart());
		// hook the viewer into the EditDomain
		getEditDomain().addViewer(viewer);
		//getGraphicalViewer().setEditDomain(this.new DefaultEditDomain(this));
		
		// activate the viewer as selection provider for Eclipse
		getSite().setSelectionProvider(viewer);
		// initialize the viewer with input
		viewer.setEditPartFactory(getEditPartFactory());
		viewer.setContents(getModel());
		
		// add context menu
		ContextMenuProvider contextMenuProvider = new WorkflowContextMenuProvider(viewer, this);
		viewer.setContextMenu(contextMenuProvider);

		getSite().registerContextMenu(contextMenuProvider, viewer);
		
		// drag'n'drop handling
		viewer.addDropTargetListener(new TextTransferDropTargetListener(viewer));
		
		return viewer;
	}
	
	@Override
	protected void setInput(IEditorInput input) {
		super.setInput(input);
		
		IRSWorkflow workflow = null;
		PersistenceManager manager;
		try {
			manager = PersistenceManager.getInstance(input);
			if (manager == null) throw new Exception(input.toString());
		} catch (Exception e) {
			Logger.handle(MessageCode.PERSISTENCE_MANAGER_PROBLEM, e, input.getName());
			return;
		}
		
		try {
			workflow = manager.getWorkflow();
			if (manager == null) throw new Exception(input.toString());
		} catch (Exception e) {
			Logger.handle(MessageCode.WORKFLOW_LOAD_DESERIALIZE_PROBLEM, e, input.getName());
			return;
		}
		
		model = workflow;
		
	}


//	private void highlightCollapsing(IRSNodeEditPart editPart) {
//		IRSNode node = editPart.getIRSNode();
//		Set<IRSNodeEditPart> newCollapsing = new LinkedHashSet<IRSNodeEditPart>();
//		if (node == null) return;
//		// XXX : a lil bit dirty
//		IRSNode parent = node.getParent();
//		if (parent == null) return;
//		if (editPart.getParent() == null) return;
//		for (Object sibling : editPart.getParent().getChildren()) {
//			if (!(sibling instanceof IRSNodeEditPart)) continue;
//			IRSNodeEditPart siblingPart = (IRSNodeEditPart) sibling;
//			if (!parent.isAncestorOf(siblingPart.getIRSNode())) continue;
//			newCollapsing.add(siblingPart);
//		}
//		
//		for (IRSNodeEditPart part : getCollapsing()) {
//			part.setHighlighted(false);
//		}
//
//		this.collapsing = newCollapsing;
//		for (IRSNodeEditPart part : newCollapsing) {
//			part.setHighlighted(true);
//		}
//	}
	
	private void updateActions(List<String> actionIDs) {
		for (String id : actionIDs) {
			IAction action = getActionRegistry().getAction(id);
			if (action != null && action instanceof UpdateAction)
				((UpdateAction) action).update();
		}
	}
	
	@SuppressWarnings("unchecked")
	public Object getAdapter(Class adapter) {
		// we need to handle common GEF elements we created

        if (adapter == GraphicalViewer.class || adapter == EditPartViewer.class) {
			return getGraphicalViewer();
		} else if (adapter == IPropertySheetPage.class) {
	        return getPropertySheetPage();
		} else if (adapter == CommandStack.class) {
			return getCommandStack();
		} else if (adapter == EditDomain.class) {
			return getEditDomain();
		} else if (adapter == ActionRegistry.class) {
			return getActionRegistry();
		} else {
			// the super implementation handles the rest
			return super.getAdapter(adapter);
		}
	}
	
	protected void firePropertyChange(int propertyId) {
		super.firePropertyChange(propertyId);
		updateActions(stackActionIDs);
//		updateActions(editorActionIDs);
	}
	
	@Override
	public String getContributorId() {
		return CONTRIBUTOR_ID;
	}
}
